
extern crate proc_macro;

use proc_macro::TokenStream;
use quote::quote;
use syn::{self, Data, DeriveInput};

#[proc_macro_derive(MyDefault)]
pub fn macro_invoke(input: TokenStream) -> TokenStream {
    let ast: DeriveInput = syn::parse(input).unwrap();    
    impl_macro(&ast)
}

fn impl_macro(ast: &DeriveInput) -> TokenStream {
    let id = &ast.ident;

    let Data::Struct(content) = &ast.data else {
        panic!("MyDefault derive macro must use in struct");
    };

    // 声明一个新的ast，用于动态构建字段赋值的token
    let mut field_ast = quote!();

    for (idx,f) in content.fields.iter().enumerate() {
        let (field_id, filed_ty) = (&f.ident, &f.ty);

        if field_id.is_none() {
            let field_idx = syn::Index::from(idx);
            field_ast.extend(quote!{
                #field_idx: #filed_ty::default(),
            });
        } else {
            field_ast.extend(quote!{
                #field_id: #filed_ty::default(),
            });
        }
    }

    quote! {
        impl Default for #id {
            fn default() -> Self {
                Self {
                    #field_ast
                }
            }
        }
    }.into()

}



#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn it_works() {
        let result = add(2, 2);
        assert_eq!(result, 4);
    }
}
